package com.nine.manage.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.nine.common.constant.VmConstants;
import com.nine.common.exception.ServiceException;
import com.nine.common.utils.DateUtils;
import com.nine.manage.domain.Emp;
import com.nine.manage.domain.Task;
import com.nine.manage.domain.TaskDetails;
import com.nine.manage.domain.VendingMachine;
import com.nine.manage.domain.dto.TaskDetailDto;
import com.nine.manage.domain.dto.TaskDto;
import com.nine.manage.domain.vo.TaskVo;
import com.nine.manage.mapper.TaskMapper;
import com.nine.manage.service.IEmpService;
import com.nine.manage.service.ITaskDetailsService;
import com.nine.manage.service.ITaskService;
import com.nine.manage.service.IVendingMachineService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 工单Service业务层处理
 *
 * @author nine
 * @date 2024-08-25
 */
@Service
public class TaskServiceImpl implements ITaskService {
	@Autowired
	private TaskMapper taskMapper;

	@Autowired
	private IVendingMachineService vendingMachineService;

	@Autowired
	private IEmpService empService;

	@Autowired
	private RedisTemplate redisTemplate;

	@Autowired
	private ITaskDetailsService taskDetailsService;

	/**
	 * 查询工单
	 *
	 * @param taskId 工单主键
	 * @return 工单
	 */
	@Override
	public Task selectTaskByTaskId(Long taskId) {
		return taskMapper.selectTaskByTaskId(taskId);
	}

	/**
	 * 查询工单列表
	 *
	 * @param task 工单
	 * @return 工单
	 */
	@Override
	public List<Task> selectTaskList(Task task) {
		return taskMapper.selectTaskList(task);
	}

	/**
	 * 新增工单
	 *
	 * @param task 工单
	 * @return 结果
	 */
	@Override
	public int insertTask(Task task) {
		task.setCreateTime(DateUtils.getNowDate());
		return taskMapper.insertTask(task);
	}

	/**
	 * 修改工单
	 *
	 * @param task 工单
	 * @return 结果
	 */
	@Override
	public int updateTask(Task task) {
		task.setUpdateTime(DateUtils.getNowDate());
		return taskMapper.updateTask(task);
	}

	/**
	 * 批量删除工单
	 *
	 * @param taskIds 需要删除的工单主键
	 * @return 结果
	 */
	@Override
	public int deleteTaskByTaskIds(Long[] taskIds) {
		return taskMapper.deleteTaskByTaskIds(taskIds);
	}

	/**
	 * 删除工单信息
	 *
	 * @param taskId 工单主键
	 * @return 结果
	 */
	@Override
	public int deleteTaskByTaskId(Long taskId) {
		return taskMapper.deleteTaskByTaskId(taskId);
	}

	/**
	 * 查询工单列表
	 *
	 * @param task
	 * @return
	 */
	@Override
	public List<TaskVo> selectTaskVoList(Task task) {
		return taskMapper.selectTaskVoList(task);
	}

	/**
	 * 新增运营、运维工单
	 *
	 * @param taskDto
	 * @return
	 */
	@Transactional
	@Override
	public int insertTaskDto(TaskDto taskDto) {
		// 1. 查询售货机是否存在
		VendingMachine vm = vendingMachineService.selectVendingMachineByInnerCode(taskDto.getInnerCode());
		if (vm == null) {
			throw new ServiceException("售货机不存在");
		}
		// 2. 校验售货机状态与 工单类型 是否相符
		checkCreateTask(vm.getVmStatus(), taskDto.getProductTypeId());
		// 3. 检查设备是否有未完成的同类型工单
		hasTask(taskDto);
		// 4. 查询并校验员工是否存在
		Emp emp = empService.selectEmpById(taskDto.getUserId());
		if (emp == null) {
			throw new ServiceException("员工不存在");
		}
		// 5. 校验员工区域是否匹配
		if (!emp.getRegionId().equals(vm.getRegionId())) {
			throw new ServiceException("员工区域不匹配");
		}
		// 6. dto 转 po，并补充属性，保存工单
		Task task = BeanUtil.copyProperties(taskDto, Task.class);
		task.setTaskStatus(VmConstants.TASK_STATUS_CREATE);
		task.setUserName(emp.getUserName());
		task.setRegionId(vm.getRegionId());
		vm.setAddr(task.getAddr());
		task.setCreateTime(DateUtils.getNowDate());
		task.setTaskCode(generateTaskCode());   // 工单编号
		int taskResult = taskMapper.insertTask(task);

		// 7. 判断是否是补货工单
		if (taskDto.getProductTypeId() == VmConstants.TASK_TYPE_SUPPLY) {
			// 8. 保存工单详情
			List<TaskDetailDto> details = taskDto.getDetails();
			if (CollUtil.isEmpty(details)) {
				throw new ServiceException("工单详情不能为空");
			}
			// 9. dto 转 po，并补充属性，保存工单详情
			List<TaskDetails> taskDetailsList = details.stream().map(dto -> {
				TaskDetails taskDetails = BeanUtil.copyProperties(dto, TaskDetails.class);
				taskDetails.setTaskId(task.getTaskId());
				return taskDetails;
			}).collect(Collectors.toList());
			taskDetailsService.batchInsertTaskDetails(taskDetailsList);
		}

		return taskResult;
	}

	/**
	 * 取消工单
	 *
	 * @param task
	 * @return
	 */
	@Override
	public int cancelTask(Task task) {
		// 1. 判断工单状态是否可以取消
		// 先根据工单id查询数据库
		Task taskDb = selectTaskByTaskId(task.getTaskId());
		// 判断工单状态是否已取消，如果是，抛异常
		if (taskDb.getTaskStatus() == VmConstants.TASK_STATUS_CANCEL) {
			throw new ServiceException("工单已取消");
		}
		// 判断工单状态是否已完成，如果是，抛异常
		if (taskDb.getTaskStatus() == VmConstants.TASK_STATUS_FINISH) {
			throw new ServiceException("工单已完成");
		}
		// 2. 设置更新字段
		task.setTaskStatus(VmConstants.TASK_STATUS_CANCEL);
		task.setUpdateTime(DateUtils.getNowDate());
		// 3. 更新工单，注意别传错了，这里是前端的task参数
		return taskMapper.updateTask(task);
	}

	// 生成并获取当天的工单编号（唯一标识）
	private String generateTaskCode() {
		String dateStr = DateUtils.getDate().replace("-", "");
		// 根据日期生成redis键
		String key = "nine.task.code." + dateStr;
		// 判断key是否存在
		if (!redisTemplate.hasKey(key)) {
			// 如果不存在，则设置初始值，并返回，同时设置过期时间为1天
			redisTemplate.opsForValue().set(key, 1, Duration.ofDays(1));
			return dateStr + "0001";
		}
		return dateStr + StrUtil.padPre(redisTemplate.opsForValue().increment(key).toString(), 4, '0');
	}

	/**
	 * 检查设备是否有未完成的同类型工单
	 *
	 * @param taskDto
	 */
	private void hasTask(TaskDto taskDto) {
		// 创建 task条件对象，并设置设备编号和工单类型，以及工单状态为进行中
		Task taskParams = new Task();
		taskParams.setInnerCode(taskDto.getInnerCode());
		taskParams.setProductTypeId(taskDto.getProductTypeId());
		taskParams.setTaskStatus(VmConstants.TASK_STATUS_PROGRESS);
		List<Task> tasks = taskMapper.selectTaskList(taskParams);
		// 同类型工单
		if (CollUtil.isNotEmpty(tasks)) {
			throw new ServiceException("设备有未完成的同类型工单，请先处理");
		}
	}

	/**
	 * 校验 售货机状态【vmStatus】 与 工单类型【productTypeId】 是否相符
	 */
	private void checkCreateTask(Long vmStatus, Long productTypeId) {
		// 如果 投放工单，设备 运行中，抛出异常
		if (productTypeId == VmConstants.TASK_TYPE_DEPLOY && vmStatus == VmConstants.VM_STATUS_RUNNING) {
			throw new ServiceException("设备运行中，不能创建投放工单");
		}
		// 如果 维修工单，设备 不在运行中，抛出异常
		if (productTypeId == VmConstants.TASK_TYPE_REPAIR && vmStatus != VmConstants.VM_STATUS_RUNNING) {
			throw new ServiceException("设备不在运行中，不能创建维修工单");
		}
		// 如果 补货工单，设备 不在运行中，抛出异常
		if (productTypeId == VmConstants.TASK_TYPE_SUPPLY && vmStatus != VmConstants.VM_STATUS_RUNNING) {
			throw new ServiceException("设备不在运行中，不能创建补货工单");
		}
		// 如果 撤机工单，设备 不在运行中，抛出异常
		if (productTypeId == VmConstants.TASK_TYPE_REVOKE && vmStatus != VmConstants.VM_STATUS_RUNNING) {
			throw new ServiceException("设备不在运行中，不能创建撤机工单");
		}
	}

}
